Skip to content

Add user identity list NATS subject#31

Open
fayazg wants to merge 1 commit intolinuxfoundation:mainfrom
fayazg:feat/lfx-user-identity-list
Open

Add user identity list NATS subject#31
fayazg wants to merge 1 commit intolinuxfoundation:mainfrom
fayazg:feat/lfx-user-identity-list

Conversation

@fayazg
Copy link

@fayazg fayazg commented Mar 24, 2026

Overview

Adds lfx.auth-service.user_identity.list — a read-only NATS subject that returns all identities linked to a user account. This is required by the lfx-v2-ui profile page to display connected social oviders (GitHub, LinkedIn, Google, etc.).

Also fixes a bug in Auth0's ToUser() which was silently dropping the Identities field when converting from the Auth0 API response to the domain model.


Changes

New handlerListIdentities in internal/service/message_handler.go

  • Accepts { "user": { "auth_token": "..." } }
  • Validates token via MetadataLookup, fetches user via GetUser
  • Returns identities mapped to a response DTO matching the UI's expected format

Bug fixinternal/infrastructure/auth0/models.go

  • ToUser() now converts Auth0Identity slice → []model.Identity (was previously ignored)

Wiring — constant, port interface, routing, subscription all updated


Test evidence

=== RUN   TestMessageHandlerOrchestrator_ListIdentities
=== RUN   TestMessageHandlerOrchestrator_ListIdentities/successful_list_with_identities
=== RUN   TestMessageHandlerOrchestrator_ListIdentities/successful_list_with_no_identities
=== RUN   TestMessageHandlerOrchestrator_ListIdentities/missing_auth_token
=== RUN   TestMessageHandlerOrchestrator_ListIdentities/invalid_json_payload
=== RUN   TestMessageHandlerOrchestrator_ListIdentities/reader_unavailable
=== RUN   TestMessageHandlerOrchestrator_ListIdentities/metadata_lookup_failure
=== RUN   TestMessageHandlerOrchestrator_ListIdentities/get_user_failure
--- PASS: TestMessageHandlerOrchestrator_ListIdentities (0.00s)
PASS

Full suite: go test ./... — all pass.


Checklist

  • UserIdentityListSubject constant added (pkg/constants/subjects.go)
  • ListIdentities added to UserReaderHandler interface (internal/domain/port/message_handler.go)
  • ListIdentities handler implemented with response DTO (internal/service/message_handler.go)
  • Auth0 ToUser() fixed to populate Identities (internal/infrastructure/auth0/models.go)
  • Handler routed in cmd/server/service/message_handler.go
  • NATS subscription registered in cmd/server/service/providers.go
  • 7 unit tests added (internal/service/message_handler_test.go)
  • Documentation updated (docs/identity_linking.md)
  • All tests pass (go test ./...)

Implement a new ListIdentities handler that retrieves all linked
identities for an authenticated user. This includes:

- New NATS subject `lfx.auth-service.user_identity.list`
- ListIdentities handler with auth token validation and identity
  mapping to a UI-friendly response format (provider, user_id,
  isSocial, profileData)
- Auth0 user model conversion now populates Identity data
- Comprehensive test coverage for success and error paths
- Updated identity linking documentation with list operation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Fayaz G. <5818912+fayazg@users.noreply.github.com>
@fayazg fayazg requested a review from a team as a code owner March 24, 2026 23:22
Copilot AI review requested due to automatic review settings March 24, 2026 23:22
@coderabbitai
Copy link

coderabbitai bot commented Mar 24, 2026

Walkthrough

This PR adds user identity listing functionality to the authentication service. It introduces a new NATS subject for identity listing, updates message routing and NATS subscriptions, extends domain interfaces, enriches Auth0 user model conversion to include identity data, implements the listing orchestrator logic, and provides comprehensive documentation and test coverage.

Changes

Cohort / File(s) Summary
Constants and Routing
pkg/constants/subjects.go, cmd/server/service/message_handler.go, cmd/server/service/providers.go
Added UserIdentityListSubject constant for the NATS subject lfx.auth-service.user_identity.list and wired it into the message handler routing map and NATS subscriptions.
Domain Interface
internal/domain/port/message_handler.go
Extended UserReaderHandler interface with new ListIdentities method accepting context and transport messenger, returning byte payload and error.
Model Enrichment
internal/infrastructure/auth0/models.go
Enhanced Auth0User.ToUser() conversion to construct model.Identity slice from Auth0 identities, mapping provider, social flag, user ID (converted to string), and conditional email/profile data.
Business Logic
internal/service/message_handler.go
Implemented ListIdentities orchestrator method with token validation, metadata lookup, user retrieval, and response transformation into structured identity list with optional profile data.
Test Coverage
internal/service/message_handler_test.go
Added comprehensive TestMessageHandlerOrchestrator_ListIdentities test suite covering success path (identity mapping, ordering, provider/user_id handling, isSocial flag, conditional profileData) and error scenarios (missing/empty token, invalid JSON, missing user reader, metadata validation failure, user retrieval failure).
Documentation
docs/identity_linking.md
Expanded documentation title and scope from "User Identity Linking" to "User Identity Operations"; added "List Identities" section defining NATS subject semantics, request/reply structure, example payloads (success and error cases), and CLI usage example.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant MessageRouter as Message Router
    participant Orchestrator as ListIdentities Handler
    participant MetadataService as Metadata Service
    participant UserReader as User Reader
    participant Auth0Converter as Auth0 Model

    Client->>MessageRouter: NATS request (auth_token)
    MessageRouter->>Orchestrator: route to ListIdentities
    Orchestrator->>Orchestrator: unmarshal & validate auth_token
    Orchestrator->>MetadataService: MetadataLookup(token)
    MetadataService-->>Orchestrator: metadata
    Orchestrator->>UserReader: GetUser(user_id)
    UserReader->>Auth0Converter: ToUser() with Identities
    Auth0Converter-->>UserReader: User with Identities[]
    UserReader-->>Orchestrator: full user record
    Orchestrator->>Orchestrator: transform to identityResponse array
    Orchestrator-->>Client: UserDataResponse (Success + Identities)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and accurately summarizes the main change: adding support for listing user identities via a new NATS subject.
Description check ✅ Passed The description is detailed and directly related to the changeset, explaining the new ListIdentities handler, the Auth0 bug fix, implementation details, test coverage, and completed checklist items.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new read-only NATS request/reply subject to list linked identities for the authenticated user (needed by the UI profile page), and fixes Auth0 user conversion so linked identities are no longer dropped.

Changes:

  • Introduces lfx.auth-service.user_identity.list subject + routes/subscribes it through the server.
  • Implements ListIdentities message handler and adds unit tests for success/error scenarios.
  • Fixes Auth0 ToUser() to map Identities into the domain model.User.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
pkg/constants/subjects.go Adds a new NATS subject constant for identity listing.
internal/domain/port/message_handler.go Extends the message handler port with ListIdentities.
internal/service/message_handler.go Implements ListIdentities handler + response DTOs.
internal/service/message_handler_test.go Adds orchestrator unit tests for the new handler.
internal/infrastructure/auth0/models.go Fixes Auth0User.ToUser() to include mapped identities.
cmd/server/service/message_handler.go Routes the new subject to the orchestrator handler.
cmd/server/service/providers.go Registers the new subject subscription.
docs/identity_linking.md Documents the new list-identities subject and payload/response format.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +295 to +296
Email: id.Email,
EmailVerified: true,
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ListIdentities sets email_verified to true whenever an identity has a non-empty email, but there’s no source-of-truth for verification status in model.Identity (only Email is stored). This can return incorrect data (e.g., for unverified provider emails) and makes the response misleading. Consider either omitting email_verified entirely (don’t set it so it’s omitted via omitempty), or extending the domain identity model to carry verification status (populated from Auth0 profileData.email_verified) and mapping that through.

Suggested change
Email: id.Email,
EmailVerified: true,
Email: id.Email,

Copilot uses AI. Check for mistakes.
Comment on lines +94 to +105
var identities []model.Identity
for _, auth0Id := range u.Identities {
identity := model.Identity{
Provider: auth0Id.Provider,
IdentityID: fmt.Sprintf("%v", auth0Id.UserID),
IsSocial: auth0Id.IsSocial,
}
if auth0Id.ProfileData != nil {
identity.Email = auth0Id.ProfileData.Email
}
identities = append(identities, identity)
}
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new ToUser() mapping now includes Identities, but there’s no unit test covering this conversion (including edge cases like non-string Auth0Identity.UserID and presence/absence of profileData). Since this is a regression-prone mapping that impacts the new user_identity.list behavior, add a focused test around Auth0User.ToUser() to assert identities are populated and correctly converted.

Copilot uses AI. Check for mistakes.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/identity_linking.md (1)

155-155: ⚠️ Potential issue | 🟡 Minor

Stale documentation: identity listing subject now exists.

This line states "there is no dedicated subject for listing identities at this time," but this PR adds exactly that capability via lfx.auth-service.user_identity.list. Please update this text to reference the new subject.

📝 Suggested update
-- `unlink.identity_id`: The identity's ID as returned by the identity provider. This must be retrieved directly from the identity provider since there is no dedicated subject for listing identities at this time.
++ `unlink.identity_id`: The identity's ID as returned by the identity provider. This can be retrieved via the `lfx.auth-service.user_identity.list` subject (see [List Identities](`#list-identities`)).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/identity_linking.md` at line 155, The documentation line that claims
"there is no dedicated subject for listing identities at this time" is now
stale—replace that sentence to mention the new subject
lfx.auth-service.user_identity.list and explain that unlink.identity_id can
alternatively be retrieved via that subject (or show how to use
lfx.auth-service.user_identity.list to list identities for a user). Update the
text around unlink.identity_id to reference lfx.auth-service.user_identity.list
as the dedicated identity-listing subject and, if appropriate, add a short note
on expected request/response semantics.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/service/message_handler.go`:
- Around line 293-298: The code currently hardcodes EmailVerified=true in
identityProfileData inside message_handler.go because model.Identity lacks an
EmailVerified field; update model.Identity to include EmailVerified bool,
propagate that value when building identities from Auth0 (use
Auth0ProfileData.EmailVerified in authelia/user.go) and in mock identities
(mock/user.go), and then change the message_handler.go block that sets
resp.ProfileData to use id.EmailVerified instead of true (or omit the field when
unknown). Ensure constructors and tests are updated to set or default
EmailVerified appropriately.

---

Outside diff comments:
In `@docs/identity_linking.md`:
- Line 155: The documentation line that claims "there is no dedicated subject
for listing identities at this time" is now stale—replace that sentence to
mention the new subject lfx.auth-service.user_identity.list and explain that
unlink.identity_id can alternatively be retrieved via that subject (or show how
to use lfx.auth-service.user_identity.list to list identities for a user).
Update the text around unlink.identity_id to reference
lfx.auth-service.user_identity.list as the dedicated identity-listing subject
and, if appropriate, add a short note on expected request/response semantics.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cf8973f7-8b70-4e38-9325-05d18b277284

📥 Commits

Reviewing files that changed from the base of the PR and between 59a1f3b and c076303.

📒 Files selected for processing (8)
  • cmd/server/service/message_handler.go
  • cmd/server/service/providers.go
  • docs/identity_linking.md
  • internal/domain/port/message_handler.go
  • internal/infrastructure/auth0/models.go
  • internal/service/message_handler.go
  • internal/service/message_handler_test.go
  • pkg/constants/subjects.go

Comment on lines +293 to +298
if id.Email != "" {
resp.ProfileData = &identityProfileData{
Email: id.Email,
EmailVerified: true,
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if email_verified is used elsewhere in identity-related code
rg -n "email_verified|EmailVerified" --type=go -C2

Repository: linuxfoundation/lfx-v2-auth-service

Length of output: 8651


🏁 Script executed:

# Find the model.Identity structure definition
fd -n "models\|identity" --type=go | grep -E "model|pkg" | head -20

Repository: linuxfoundation/lfx-v2-auth-service

Length of output: 309


🏁 Script executed:

# Get full context around the problematic lines in message_handler.go
cat -n internal/service/message_handler.go | sed -n '280,310p'

Repository: linuxfoundation/lfx-v2-auth-service

Length of output: 1024


🏁 Script executed:

# Search for model.Identity definition
rg -n "type Identity struct" --type=go -A 15

Repository: linuxfoundation/lfx-v2-auth-service

Length of output: 1476


🏁 Script executed:

# Find where fullUser.Identities is populated
rg -n "Identities\s*=" --type=go -B 5 -A 5 | head -80

Repository: linuxfoundation/lfx-v2-auth-service

Length of output: 4356


🏁 Script executed:

# Check how Auth0 identities are mapped to model.Identity
rg -n "model\.Identity|Identity{" --type=go -B 3 -A 3 | grep -A 8 -B 8 "Email"

Repository: linuxfoundation/lfx-v2-auth-service

Length of output: 192


🏁 Script executed:

# Look at Auth0 integration to understand when emails are unverified
rg -n "EmailVerified.*false" --type=go -B 3 -A 3

Repository: linuxfoundation/lfx-v2-auth-service

Length of output: 1739


🏁 Script executed:

# Check API endpoint that returns this data
rg -n "GetIdentities|identities\|email_verified" --type=go | head -40

Repository: linuxfoundation/lfx-v2-auth-service

Length of output: 61


🏁 Script executed:

# Search for tests that verify the email_verified response field
rg -n "email_verified.*true|email_verified.*false" --type=go -B 3 -A 3

Repository: linuxfoundation/lfx-v2-auth-service

Length of output: 519


EmailVerified hardcoded as true due to missing field in model.Identity.

The issue is real: Auth0ProfileData includes EmailVerified (which can be false for passwordless flows), but model.Identity lacks this field entirely. When identities are constructed in authelia/user.go and mock/user.go, the EmailVerified status is discarded. The response then hardcodes true for any identity with an email, misrepresenting unverified emails as verified.

Consider extending model.Identity to include EmailVerified and propagating it from Auth0, or omit email_verified from the response if accurate status cannot be provided.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/service/message_handler.go` around lines 293 - 298, The code
currently hardcodes EmailVerified=true in identityProfileData inside
message_handler.go because model.Identity lacks an EmailVerified field; update
model.Identity to include EmailVerified bool, propagate that value when building
identities from Auth0 (use Auth0ProfileData.EmailVerified in authelia/user.go)
and in mock identities (mock/user.go), and then change the message_handler.go
block that sets resp.ProfileData to use id.EmailVerified instead of true (or
omit the field when unknown). Ensure constructors and tests are updated to set
or default EmailVerified appropriately.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants